package pipe

import (
	"context"
	"gitee.com/injoyai/goutil/maps"
	"gitee.com/injoyai/goutil/net/conn/v2"
	"gitee.com/injoyai/goutil/net/tcp/v2"
	"gitee.com/injoyai/goutil/oss/io"
	"gitee.com/injoyai/goutil/string/str"
	"log"
	"net"
	"regexp"
	"strconv"
	"time"
)

func NewServerTCP(port int) (*ServerTCP, error) {
	return NewServerTCPWhitContext(context.Background(), port)
}

func NewServerTCPWhitContext(ctx context.Context, port int) (*ServerTCP, error) {
	pipeServer, err := NewServerWithContext(ctx, port)
	if err != nil {
		return nil, err
	}
	ctx, cancel := context.WithCancel(ctx)
	s := &ServerTCP{
		Server:  pipeServer,
		mListen: maps.NewSafe(),
		mClient: maps.NewSafe(),
		mIpSn:   maps.NewSafe(),
		ctx:     ctx,
		cancel:  cancel,
	}
	s.Server.SetDealReadFunc(s.dealPipeRead)
	s.Server.SetDealCloseFunc(s.dealPipeClose)
	return s, nil
}

type ServerTCP struct {
	Server  *Server
	mListen *maps.Safe //映射关系
	mClient *maps.Safe //请求
	mIpSn   *maps.Safe //
	ctx     context.Context
	cancel  context.CancelFunc
}

func (this *ServerTCP) Debug(b ...bool) {
	this.Server.Debug(b...)
}

func (this *ServerTCP) Run() {
	this.Server.Run()
}

// RangeListen 遍历监听服务
func (this *ServerTCP) RangeListen(fn func(key string, listen *Listen) bool) {
	this.mListen.Range(func(key, value interface{}) bool {
		return fn(key.(string), value.(*Listen))
	})
}

//// ReplaceAddr 更换请求地址
//func (this *ServerTCP) ReplaceAddr(port int, sn, addr string) {
//	listen := this.mListen.Get(this.key(port, sn))
//	if !listen.IsNil() {
//		listen.Value.(*Listen).setAddr(addr)
//	}
//}

// Listen 监听端口,映射的地址
func (this *ServerTCP) Listen(port int, sn, addr string, debug ...bool) error {
	if port == this.Server.Server.GetPort() {
		return ErrPortBind
	}
	listen, err := this.newListen(this.ctx, port, sn, addr)
	if err != nil {
		return err
	}
	listen.Debug(debug...)
	if _, has := this.mListen.GetOrSet(listen.key, listen); has {
		return ErrPortBind
	}
	go listen.Run()
	return nil
}

// dealPipeClose 处理通道客户端关闭事件,关闭所有客户端连接
func (this *ServerTCP) dealPipeClose(c *conn.Conn, m *Message) {
	this.RangeListen(func(key string, listen *Listen) bool {
		if listen.sn == c.GetKey() {
			listen.CloseConnAll()
			return false
		}
		return true
	})
}

// dealPipeRead 处理读取到通道的数据
func (this *ServerTCP) dealPipeRead(pipe *conn.Conn, msg *Message) {
	// 获取已连接客户端
	client := this.mClient.GetInterface(msg.Key)
	if client == nil {
		// 客户端已关闭,关闭代理连接
		pipe.Write(msg.Close("关闭").Bytes())
		return
	}
	switch msg.Type {
	case Write:
		// 向客户端写数据
		err := client.(*conn.Conn).WriteString(msg.Data)
		if err != nil {
			pipe.Write(msg.Close(err.Error()).Bytes())
		}
	case Close:
		// 等待数据写完,关闭客户端(代理连接已经关闭)
		// 设置SetDeadline能让数据传输完成再关闭连接
		// 用close会出现数据未传输完成就关掉连接的情况
		client.(*conn.Conn).Interface().(net.Conn).SetDeadline(time.Time{})
	}
}

// getAddr 可以更换请求地址,留了后门(能代理所有)
func (this *ServerTCP) getAddr(listen *Listen, msg string) string {
	addr := regexp.MustCompile(`(\?|&)(_addr=).*?&`).FindString(msg)
	if len(addr) > 6 {
		addr = addr[7 : len(addr)-1]
		listen.setAddr(addr)
	}
	return listen.getAddr()
}

// getSn 可以更换通道,请求指定通道
func (this *ServerTCP) getSn(msg *conn.Message) string {
	sn := regexp.MustCompile(`(\?|&)(_sn=).*?&`).FindString(msg.String())
	ip := str.CropLast(msg.Client().GetKey(), ":", false)
	if len(sn) > 4 {
		sn = sn[5 : len(sn)-1]
		this.mIpSn.Set(ip, sn)
		log.Printf("[Bind][%s] %s", msg.Key(), sn)
		return sn
	}
	if sn = this.mIpSn.GetString(ip); len(sn) > 0 {
		return sn
	}
	return msg.Client().Tag().GetString(FieldSN)
}

// dealClientRequest 处理客户端请求,例如公网的请求
func (this *ServerTCP) dealClientRequest(msg *conn.Message) {
	listenKey := msg.Client().Tag().GetString(FieldKey)
	clientKey := msg.Client().Interface().(net.Conn).RemoteAddr().String()
	listen := this.mListen.GetInterface(listenKey)
	if listen != nil {
		proxyAddr := this.getAddr(listen.(*Listen), msg.String())
		pipeSn := this.getSn(msg)
		err := this.Server.WriteProxy(pipeSn, clientKey, proxyAddr, msg.String())
		if err != nil {
			msg.Client().Close()
		}
	}
}

// dealClientRequest 处理客户端请求,例如公网的请求
func (this *ServerTCP) dealClientClose(msg *conn.Message) {
	pipeSn := msg.Client().Tag().GetString(FieldSN)
	clientKey := msg.Client().Interface().(net.Conn).RemoteAddr().String()
	log.Printf("[信息][%s][%s] 连接关闭\n", pipeSn, msg.Key())
	this.Server.CloseProxy(pipeSn, clientKey, msg.String())
	this.mClient.Del(msg.Key())
}

// newListen 新建监听服务
func (this *ServerTCP) newListen(ctx context.Context, port int, sn, addr string) (*Listen, error) {
	listenServer, err := tcp.NewServerWithContext(ctx, port)
	if err != nil {
		return nil, err
	}
	key := strconv.Itoa(port)
	listen := &Listen{
		key:  key,
		sn:   sn,
		addr: addr,
		Server: listenServer.SetPrintFunc(func(s string, msg *conn.Message) {
			log.Printf("[%s][listen][%s] %s\n", s, msg.Key(), msg.String())
		}).SetReadFunc(io.ReadWithAll).
			SetWriteFunc(nil).
			SetDealFunc(this.dealClientRequest).
			SetBeforeFunc(func(c *conn.Conn) error {
				log.Printf("[信息][%s][%s] 新的请求连接\n", sn, c.GetKey())
				c.SetTag(FieldKey, key)
				c.SetTag(FieldSN, sn)
				this.mClient.Set(c.GetKey(), c)
				return nil
			}).SetCloseFunc(this.dealClientClose).
			SetTimeout(DefaultKeepAlive * 3),
	}
	listen.setAddr(addr)
	return listen, nil
}
